package org.moonlight.jvm.classfile;

import java.math.BigInteger;

/**
 * 解析，读取，操作 类文件中读取出来的数据
 *
 * @author Moonlight
 * @createTime 2023/8/14 17:08
 **/
public class ClassReader {

    /** 从类文件中读取出来的数据 **/
    private final byte[] data;

    public ClassReader(byte[] data) {
        this.data = data;
    }

    /**
     * 读取 u1 类型
     * @return int
     */
    public int readUint8() {
        byte[] val = readBytes(1);
        return bytes2int(val);
    }

    /**
     * 读取 u2 类型
     * @return int
     */
    public int readUint16() {
        byte[] val = readBytes(2);
        return bytes2int(val);
    }

    /**
     * 读取 u4 类型
     * @return int
     */
    public long readUint32() {
        byte[] val = readBytes(4);
//        return bytes2int(val);
        String strHex = new BigInteger(1, val).toString(16);
        return Long.parseLong(strHex, 16);
    }

    private int bytes2int(byte[] val) {
        return new BigInteger(1, val).intValue();
    }

    public int readUint32ToInteger() {
        byte[] val = readBytes(4);
        return new BigInteger(1, val).intValue();
    }

    public float readUint32ToFloat() {
        byte[] val = readBytes(4);
        return new BigInteger(1, val).floatValue();
    }

    public long readUint64ToLong() {
        byte[] val = readBytes(8);
        return new BigInteger(1, val).longValue();
    }

    public double readUint64ToDouble() {
        byte[] val = readBytes(8);
        return new BigInteger(1, val).doubleValue();
    }

    /**
     * 先读取一个 uint16 它指明了数组的长度 n ，依据这个 长度 往下读取 n * uint16 组成一个 uint16 数组
     * @return int[]
     */
    public int[] readUint16s() {
        int n = this.readUint16();
        int[] s = new int[n];
        for (int i = 0; i < n; i++) {
            s[i] = this.readUint16();
        }
        return s;
    }

    /**
     * 读取指定长度的字节数据，并将读取到的字节数据组织成一个字节数组返回
     * @param len 长度. 读取多少长度的字节
     * @return byte[] 字节数组
     * @createTime 18:06 2023/8/14
     * @author moonlight
     **/
    public byte[] readBytes(int len) {
        byte[] read = new byte[len];
        System.arraycopy(this.data, 0, read, 0, len);
        // 把后面没有读的字节复制到前面来，这样子就不用记录下标，也不用重新创建数组了
        System.arraycopy(this.data, len, this.data, 0, this.data.length - len);
        return read;
    }

}
